#include "gm510.h"
#include "tos_at.h"

#include "sal_module_wrapper.h"
#include "usart.h"
#include <stdio.h>
#include <stdbool.h>
#include <ctype.h>
#include <string.h>
#include "fsapi.h"
#include "data.h"

static uint8_t init_state = 0;
uint8_t connect_fail_cnt = 0;

static int gm510_signal_ati(void)
{
    int try = 0;

    at_echo_t echo;
    char echo_buffer[32];
    
    while (try++ < 3){
        tos_at_echo_create(&echo, echo_buffer, sizeof(echo_buffer), NULL);
        tos_at_cmd_exec(&echo, 1000, "ATI\r");
        if (echo.status == AT_ECHO_STATUS_OK) {
            printf("%s", echo.buffer);
        }
        os_msleep(1000);        
    }
    return -1;
}
#if 0
static int gm510_rate_set(void)
{
    at_echo_t echo;

    tos_at_echo_create(&echo, NULL, 0, NULL);
    tos_at_cmd_exec(&echo, 1000, "AT+IPR=115200\r");
    if (echo.status == AT_ECHO_STATUS_OK) {
        return 0;
    }
    os_msleep(1000);        

    return -1;
}
#endif
static int gm510_cpin_check(void)
{
    at_echo_t echo;
    int try = 0;
    while (try++ < 5) {
        tos_at_echo_create(&echo, NULL, 0, "+CPIN: READY");
        tos_at_cmd_exec(&echo, 2000, "AT+CPIN?\r");
        if (echo.status == AT_ECHO_STATUS_OK || echo.status == AT_ECHO_STATUS_EXPECT) {
            return 0;
        }
        os_msleep(500);
    }
    return -1;
}

static int gm510_cereg_check(void)
{
    int try = 0;
    at_echo_t echo;
    while (try++ < 15) {
        tos_at_echo_create(&echo, NULL, 0, "+CEREG: 0,1");
        tos_at_cmd_exec(&echo, 2000, "AT+CEREG?\r");
        if (echo.status == AT_ECHO_STATUS_EXPECT) {
            return 0;
        }
        os_msleep(1000);
    }
    return -1;
}

static int gm510_zipcfg_set(void)
{
    int try = 0;
    at_echo_t echo;
    while (try++ < 2) {
        tos_at_echo_create(&echo, NULL, 0, NULL);
        tos_at_cmd_exec(&echo, 2000, "AT+ZIPCFG=ctnet,user,1234\r");
        if (echo.status == AT_ECHO_STATUS_OK) {
            return 0;
        }
        os_msleep(500);
    }
    return -1;
}

static int gm510_zipcall_set(void)
{
    int try = 0;
    at_echo_t echo;   
    
    while (try++ < 3) {
        tos_at_echo_create(&echo, NULL, 0, "+ZIPCALL:");
        tos_at_cmd_exec(&echo, 2000, "AT+ZIPCALL=1\r");
        if (echo.status == AT_ECHO_STATUS_OK || echo.status == AT_ECHO_STATUS_EXPECT) {
            return 0;
        }
        os_msleep(500);
    }
    return -1;
}
#if 0
static int gm510_atlog_set(void)
{
    at_echo_t echo;   
    tos_at_echo_create(&echo, NULL, 0, NULL);
    tos_at_cmd_exec(&echo, 1000, "AT+LOG=16,529\r");
    if (echo.status == AT_ECHO_STATUS_OK ) {
        return 0;
    }
    
    tos_at_echo_create(&echo, NULL, 0, NULL);
    tos_at_cmd_exec(&echo, 1000, "AT+MEDCR=0,1,200\r");
    if (echo.status == AT_ECHO_STATUS_OK ) {
        return 0;
    }    

    return -1;
}
#endif
static int gm510_signal_quality_check(void)
{
    int try = 0;
    int rssi, ber;
    at_echo_t echo;
    char echo_buffer[32], *str;
    
    while (try++ < 3){
        tos_at_echo_create(&echo, echo_buffer, sizeof(echo_buffer), NULL);
        tos_at_cmd_exec(&echo, 1000, "AT+CSQ\r");
        if (echo.status == AT_ECHO_STATUS_OK) {
            str = strstr(echo.buffer, "+CSQ:");
            sscanf(str, "+CSQ:%d,%d", &rssi, &ber);
            if (rssi != 99) {
                return 0;
            }
        }
        os_msleep(1000);        
    }
    return -1;
}

static int gm510_init(void)
{
    printf("Init gm510 ...\n" );

    gm510_signal_ati();
    
    if (gm510_cpin_check() != 0){
        printf("cpin check failed\n");
        return -1;
    }
    
    //gm510_rate_set();

    if (gm510_signal_quality_check() != 0) {
        printf("signal quality check status failed\n");
        return -1;
    }
    
    if (gm510_cereg_check() != 0){
        printf("cereg check failed\n");
        return -1;
    }
    
    if (gm510_zipcfg_set() != 0){
        printf("zipcfg set failed\n");
        return -1;
    }
                            
    if (gm510_zipcall_set() != 0){
        printf("zipcall set failed\n");
        return -1;
    }
    //gm510_atlog_set();
    printf("Init gm510 ok\n" );
    return 0;
}

int gm510_close(int id);
static int gm510_connect(const char *ip, const char *port, sal_proto_t proto)
{
    int id;
    at_echo_t echo;
    int32_t ipval[4]={0};
    if (init_state == 0){
        return -1;
    }

    sscanf(ip, "%d.%d.%d.%d", &ipval[0], &ipval[1], &ipval[2], &ipval[3]);   
    id = tos_at_channel_alloc(ip, port);
    if (id == -1) {
        return -1;
    }
    
    if (tos_at_global_lock_pend() != 0) {
        tos_at_channel_free(id);
        return -1;
    }
    
    tos_at_echo_create(&echo, NULL, 0, NULL);

    tos_at_cmd_exec(&echo, 5000, "AT+ZIPOPEN=%d,%d,%d.%d.%d.%d,%s\r",
                        id, proto == TOS_SAL_PROTO_UDP ? 1 : 0, ipval[0],ipval[1],ipval[2],ipval[3], port);
    if (echo.status == AT_ECHO_STATUS_OK) {
        connect_fail_cnt = 0;
        os_msleep(1000);
        tos_at_global_lock_post();
        return id;
    }

    tos_at_channel_free(id);
    tos_at_global_lock_post();
    
    gm510_close(id);
    
    connect_fail_cnt++;
    if (connect_fail_cnt > 3){
        connect_fail_cnt = 0;
        init_state = 0;
    }
    return -1;
    
}

static int gm510_recv_timeout(int id, void *buf, size_t len, uint32_t timeout)
{
    return tos_at_channel_read_timed(id, buf, len, timeout);
}

static int gm510_recv(int id, void *buf, size_t len)
{
    return gm510_recv_timeout(id, buf, len, (uint32_t)4000);
}

int gm510_send(int id, const void *buf, size_t len)
{
    at_echo_t echo;

    if (tos_at_global_lock_pend() != 0) {
        printf("gm510_send get muxlock err\n");
        return -1;
    }

    tos_at_echo_create(&echo, NULL, 0, ">");
    tos_at_cmd_exec(&echo, 2000,
                            "AT+ZIPSENDRAW=%d,%d\r",
                            id, len);

    if (echo.status != AT_ECHO_STATUS_OK && echo.status != AT_ECHO_STATUS_EXPECT) {
        tos_at_global_lock_post();
        return -1;
    }
    //char_t time[30]={0};
    //rtc_get_currt_time_str(time, sizeof(time));
    //printf("time:%s\n", time);

    tos_at_echo_create(&echo, NULL, 0, NULL);
    tos_at_raw_data_send(&echo, 3000, (uint8_t *)buf, len);
    if (echo.status != AT_ECHO_STATUS_OK) {
        tos_at_global_lock_post();
        return -1;
    }
    os_msleep(500);
    tos_at_global_lock_post();

    return len;
}

static int gm510_close(int id)
{
    at_echo_t echo;
    tos_at_echo_create(&echo, NULL, 0, NULL);
    tos_at_cmd_exec(&echo, 5000, "AT+ZIPCLOSE=%d\r", id);

    tos_at_channel_free(id);

    return 0;
}

static int gm510_parse_domain(const char *host_name, char *host_ip, size_t host_ip_len)
{
    char *str;
    at_echo_t echo;
    char echo_buffer[64];

    tos_at_echo_create(&echo, echo_buffer, sizeof(echo_buffer), NULL);
    tos_at_cmd_exec(&echo, 2000, "AT+ZDNSGETIP=\"%s\"\r", host_name);

    if (echo.status != AT_ECHO_STATUS_OK) {
        return -1;
    }

    /*
    +CDNSGIP: 1,"www.xyz.com","xxx.xxx.xxx.xxx","xxx.xxx.xxx.xxx"
     */

    int seg1, seg2, seg3, seg4;
    str = strstr(echo.buffer, "+CDNSGIP: 1,");
    str += strlen("+CDNSGIP: 1,\"") + strlen(host_name) + 3;
    sscanf(str, "%d.%d.%d.%d", &seg1, &seg2, &seg3, &seg4);
    snprintf(host_ip, host_ip_len, "%d.%d.%d.%d", seg1, seg2, seg3, seg4);
    host_ip[host_ip_len - 1] = '\0';

    printf("GOT IP: %s\n", host_ip);

    return 0;
}

static uint8_t incoming_data_buffer[1500];

//"+ZIPRECV: %d,%d.%d.%d.%d,%d,%d,", &socket, &ip1, &ip2, &ip3, &ip4, &port, &data_len
static void gm510_incoming_data_process(void)
{
    uint8_t data;
    int channel_id = 0, data_len = 0;

    //socket
    while (1) {
        if (tos_at_uart_read(&data, 1) != 1) {
            printf("read socket failed");
            return;
        }
        if (data == ',') {
            break;
        }
        channel_id = channel_id * 10 + (data - '0');
    }
    //ip
    while (1) {
        if (tos_at_uart_read(&data, 1) != 1) {
            printf("read ip failed\r\n");
            return;
        }
        if (data == ',') {
            break;
        }
    }
    //port
    while (1) {
        if (tos_at_uart_read(&data, 1) != 1) {
            printf("read port failed\r\n");
            return;
        }
        if (data == ',') {
            break;
        }
    }    
    //len
    while (1) {
        if (tos_at_uart_read(&data, 1) != 1) {
            printf("read len failed\r\n");
            return;
        }
        if (data == ',') {
            break;
        }
        data_len = data_len * 10 + (data - '0');
    }
    
    if (data_len > sizeof(incoming_data_buffer)) {
        data_len = sizeof(incoming_data_buffer);
    }

    if (tos_at_uart_readarray(incoming_data_buffer, data_len) != data_len) {
        printf("gm510_incoming_data_process 555555555555555555555555\n");
        return;
    }
    //printf("gm510_incoming_data_process id:%d data len:%d\n", channel_id, data_len);
    tos_at_channel_write(channel_id, incoming_data_buffer, data_len);
}

at_event_t gm510_at_event[] = {
    { "+ZIPRECV: ", gm510_incoming_data_process },
};

sal_module_t sal_module_gm510 = {
    .init           = gm510_init,
    .connect        = gm510_connect,
    .send           = gm510_send,
    .recv_timeout   = gm510_recv_timeout,
    .recv           = gm510_recv,
    .close          = gm510_close,
    .parse_domain   = gm510_parse_domain,
};

#include "port.h"
static void NetW4g_power_on() {
    w4g_power_low();
    os_msleep(200);
    w4g_power_high();
    os_msleep(1000);
}

static void NetW4g_Reset() {
    printf("gm510 reset...\n");
    w4g_reset_low();
    os_msleep(200);
    w4g_reset_high();
    os_msleep(8000);
}

void gm510_check_task(void *para){
    uint8_t init_cnt = 0;
    uint32_t time_cnt = 0;
    while(1){
        
        if (time_cnt %100 == 0){
            if(init_state == 0){
                
                if (tos_sal_module_init() == 0) {
                    init_state = 1;
                }else{
                    init_cnt++;
                    if (init_cnt > 2){
                        init_cnt = 0;
                        NetW4g_Reset();
                    }
                }         
            }else{
                
            }        
        }
        
        os_msleep(10);
    }
}

int gm510_sal_init(hal_uart_port_t uart_port)
{
    usart3_init(115200);
    w4g_port_init();
    NetW4g_power_on();
    os_pthread_t check_pthread;
    
    if (tos_at_init(uart_port, gm510_at_event,
                        sizeof(gm510_at_event) / sizeof(gm510_at_event[0])) != 0) {
        return -1;
    }

    if (tos_sal_module_register(&sal_module_gm510) != 0) {
        return -1;
    }
    
    if (os_pthread_create("link_check", &check_pthread, gm510_check_task, NULL,  OS_TASK_PRIO_LOW, OS_STACK_SIZE_LARGE) != 0){
        return -1;
    }    

    return 0;
}

